﻿using System;
using System.Collections.Generic;

namespace HEFramework
{
    [Flags]
    public enum eEntityStatus : byte
    {
        None = 0,
        IsRegister = 1 << 1,
        IsCreated = 1 << 3,
        IsNew = 1 << 4,
    }


    /// <summary>
    ///
    /// 实体基类
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-12-21 14:54:23
    /// ----------------------------------------
    /// </summary>
    public class Entity : DisposeObject
    {
#if DEBUG_SHOW && UNITY_EDITOR
        [UnityEngine.HideInInspector] private UnityEngine.GameObject viewGo;
#endif

        private eEntityStatus status = eEntityStatus.None;
        private Entity parent;
        private Dictionary<long, Entity> childs;
        private Dictionary<Type, long> mappings;
        protected IScene scene;


        public long InstanceID { get; protected set; }
        public bool IsDisposed => this.InstanceID == 0;

        public Dictionary<long, Entity> Childs
        {
            get { return this.childs ??= ObjectGenerater.Instance.Fetch<Dictionary<long, Entity>>(); }
        }

        public Dictionary<Type, long> Mappings
        {
            get { return this.mappings ??= ObjectGenerater.Instance.Fetch<Dictionary<Type, long>>(); }
        }

        protected bool IsRegister
        {
            get => (this.status & eEntityStatus.IsRegister) == eEntityStatus.IsRegister;
            set
            {
                if (this.IsRegister == value)
                {
                    return;
                }

                if (value)
                {
                    this.status |= eEntityStatus.IsRegister;
                }
                else
                {
                    this.status &= ~eEntityStatus.IsRegister;
                }

                if (value)
                {
                    this.Register();
                }

#if DEBUG_SHOW && UNITY_EDITOR
                if (value)
                {
                    this.viewGo = new UnityEngine.GameObject(this.GetType().FullName);
                    if (this is IScene s)
                    {
                        this.viewGo.transform.name += $"[{s.SceneType}]";
                    }

                    this.viewGo.transform.SetParent(this.Parent == null ? UnityEngine.GameObject.Find("Scenes").transform : this.Parent.viewGo.transform);
                }
                else
                {
                    UnityEngine.Object.Destroy(this.viewGo);
                }
#endif
            }
        }


        protected bool IsCreated
        {
            get => (this.status & eEntityStatus.IsCreated) == eEntityStatus.IsCreated;
            set
            {
                if (value)
                {
                    this.status |= eEntityStatus.IsCreated;
                }
                else
                {
                    this.status &= ~eEntityStatus.IsCreated;
                }
            }
        }


        protected bool IsNew
        {
            get => (this.status & eEntityStatus.IsNew) == eEntityStatus.IsNew;
            set
            {
                if (value)
                {
                    this.status |= eEntityStatus.IsNew;
                }
                else
                {
                    this.status &= ~eEntityStatus.IsNew;
                }
            }
        }


        public Entity Parent
        {
            get => this.parent;
            protected set
            {
                if (value == null)
                {
                    throw new Exception($"Cant set parent null: {this.GetType().FullName}");
                }

                if (value == this)
                {
                    throw new Exception($"Cant set parent self: {this.GetType().FullName}");
                }

                // 严格限制parent必须要有domain,也就是说parent必须在数据树上面
                if (value.IScene == null)
                {
                    throw new Exception($"Cant set parent because parent domain is null: {this.GetType().FullName} {value.GetType().FullName}");
                }

                if (this.parent != null) // 之前有parent
                {
                    // parent相同，不设置
                    if (this.parent == value)
                    {
                        Log.Error($"Same parent -> My: {this.GetType().FullName} Parent: {this.parent.GetType().FullName}");
                        return;
                    }

                    this.parent.RemoveFromChild(this);
                }

                this.parent = value;
                this.parent.AddToChild(this);

                if (this is IScene scene)
                {
                    this.IScene = scene;
                }
                else
                {
                    this.IScene = this.parent.scene;
                }

#if DEBUG_SHOW && UNITY_EDITOR
                this.viewGo.transform.SetParent(this.Parent == null ? UnityEngine.GameObject.Find("Scenes").transform : this.Parent.viewGo.transform);
                foreach (var child in this.Childs.Values)
                {
                    child.viewGo.transform.SetParent(this.viewGo.transform);
                }
#endif
            }
        }


        public IScene IScene
        {
            get { return this.scene; }
            protected set
            {
                if (value == null)
                {
                    throw new Exception($"Domain cant set null: {this.GetType().FullName}");
                }

                if (this.scene == value)
                {
                    return;
                }

                IScene preScene = this.scene;
                this.scene = value;

                if (preScene == null)
                {
                    if (this.InstanceID == 0)
                    {
                        this.InstanceID = IDGenerater.Instance.GenerateInstanceID();
                    }

                    this.IsRegister = true;
                }

                // 递归设置孩子的Domain
                if (this.childs != null)
                {
                    foreach (Entity entity in this.childs.Values)
                    {
                        entity.IScene = this.scene;
                    }
                }


                if (!this.IsCreated)
                {
                    this.IsCreated = true;
                    SystemOwner.Instance.Deserialize(this);
                }
            }
        }


        protected Entity()
        {
        }


        protected virtual void Register()
        {
            World.Instance.EntitySystem.Register(this);
        }


        public override void Dispose()
        {
            if (this.IsDisposed)
            {
                return;
            }

            ObjectGenerater pool = ObjectGenerater.Instance;
            // 清理Childs
            if (this.childs != null)
            {
                if (this.childs.Count > 0)
                {
                    using ListObject<Entity> temps = ListObject<Entity>.Create();
                    foreach (Entity child in this.childs.Values)
                    {
                        temps.Add(child);
                    }

                    for (int i = 0; i < temps.Count; i++)
                    {
                        temps[i].Dispose();
                    }
                }
                else
                {
                    ObjectGenerater.Instance.Recycle(this.childs);
                    this.childs = null;
                }
            }

            //清理Mappings
            if (this.mappings != null)
            {
                this.mappings.Clear();
                ObjectGenerater.Instance.Recycle(this.mappings);
            }


            // 触发Destroy事件
            if (this is IDestroy)
            {
                SystemOwner.Instance.Destroy(this);
            }


            if (this.parent != null && !this.parent.IsDisposed)
            {
                this.parent.RemoveFromChild(this);
            }


            this.IsRegister = false;
            this.InstanceID = 0;
            this.scene = null;
            this.parent = null;
            status = eEntityStatus.None;

            if (this.IsFromPool)
            {
                pool.Recycle(this);
            }
        }


        public int ChildCount()
        {
            if (this.childs == null)
            {
                return 0;
            }

            return this.childs.Count;
        }

        public T GetParent<T>() where T : Entity
        {
            return this.Parent as T;
        }

        public Entity GetScene()
        {
            return IScene as Entity;
        }

        public K GetChild<K>(long _id) where K : Entity
        {
            if (this.childs == null)
            {
                return null;
            }

            this.childs.TryGetValue(_id, out Entity child);
            return child as K;
        }

        public K GetChild<K>() where K : Entity
        {
            if (this.childs == null)
            {
                return null;
            }

            if (this.mappings == null)
            {
                return null;
            }

            K c = GetMapping<K>();
            if (c != null)
            {
                return c;
            }
            else
            {
                foreach (var child in childs.Values)
                {
                    if (child is K kChild)
                    {
                        AddMapping<K>(kChild);
                        return kChild;
                    }
                }
            }

            return null;
        }

        public K GetParentChild<K>() where K : Entity
        {
            return this.parent.GetChild<K>();
        }

        public K GetSceneChild<K>() where K : Entity
        {
            return GetScene().GetChild<K>();
        }

        /// <summary>
        /// 返回的是一个全新的数组
        /// </summary>
        /// <typeparam name="K"></typeparam>
        /// <returns></returns>
        public K[] GetChildArray<K>() where K : Entity
        {
            if (childs == null || childs.Count == 0)
            {
                return new K[0];
            }

            using ListObject<K> resultList = ListObject<K>.Create();
            foreach (var child in childs.Values)
            {
                if (child is K kChild)
                {
                    resultList.Add(kChild);
                }
            }

            var results = resultList.ToArray();
            return results;
        }

        /// <summary>
        /// 记得手动Dispose/使用using语法释放List,否则生成的列表不会回收
        /// </summary>
        /// <typeparam name="K"></typeparam>
        /// <returns></returns>
        public ListObject<K> GetChildList<K>() where K : Entity
        {
            ListObject<K> resultList = ListObject<K>.Create();

            if (childs == null || childs.Count == 0)
            {
                return resultList;
            }

            foreach (var child in childs.Values)
            {
                if (child is K kChild)
                {
                    resultList.Add(kChild);
                }
            }

            return resultList;
        }

        public Entity AddChild(Entity _entity)
        {
            _entity.Parent = this;
            return _entity;
        }

        public T AddChild<T>(bool _isFromPool = false) where T : Entity, IAwake
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = IDGenerater.Instance.GenerateInstanceID();
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity);
            return entity;
        }

        public T AddChild<T, A>(A _a, bool _isFromPool = false) where T : Entity, IAwake<A>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = IDGenerater.Instance.GenerateInstanceID();
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a);
            return entity;
        }

        public T AddChild<T, A, B>(A _a, B _b, bool _isFromPool = false) where T : Entity, IAwake<A, B>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = IDGenerater.Instance.GenerateInstanceID();
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a, _b);
            return entity;
        }

        public T AddChild<T, A, B, C>(A _a, B _b, C _c, bool _isFromPool = false) where T : Entity, IAwake<A, B, C>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = IDGenerater.Instance.GenerateInstanceID();
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a, _b, _c);
            return entity;
        }

        public T AddChildWithId<T>(long _id, bool _isFromPool = false) where T : Entity, IAwake
        {
            Type type = typeof(T);
            T entity = Create(type, _isFromPool) as T;
            entity.InstanceID = _id;
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity);
            return entity;
        }

        public T AddChildWithId<T, A>(long _id, A _a, bool _isFromPool = false) where T : Entity, IAwake<A>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = _id;
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a);
            return entity;
        }

        public T AddChildWithId<T, A, B>(long _id, A _a, B _b, bool _isFromPool = false) where T : Entity, IAwake<A, B>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = _id;
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a, _b);
            return entity;
        }

        public T AddChildWithId<T, A, B, C>(long _id, A _a, B _b, C _c, bool _isFromPool = false) where T : Entity, IAwake<A, B, C>
        {
            Type type = typeof(T);
            T entity = (T)Create(type, _isFromPool);
            entity.InstanceID = _id;
            entity.Parent = this;
            SystemOwner.Instance.Awake(entity, _a, _b, _c);
            return entity;
        }


        private K GetMapping<K>() where K : Entity
        {
            Type type = typeof(K);
            if (mappings.TryGetValue(type, out long id) && id != 0 && childs.TryGetValue(id, out Entity child))
            {
                return child as K;
            }

            return null;
        }


        private void AddMapping<K>(Entity _entity) where K : Entity
        {
            Type type = typeof(K);
            mappings[type] = _entity.InstanceID;
        }

        private void AddToChild(Entity _entity)
        {
            this.Childs.Add(_entity.InstanceID, _entity);
            int count = this.Mappings.Count;
        }

        private void RemoveFromChild(Entity _entity)
        {
            if (this.childs == null)
            {
                return;
            }

            this.childs.Remove(_entity.InstanceID);

            if (this.childs.Count == 0)
            {
                ObjectGenerater.Instance.Recycle(this.childs);
                this.childs = null;
            }
        }


        private Entity Create(Type _type, bool _isFromPool)
        {
            Entity entity;
            if (_isFromPool)
            {
                entity = (Entity)ObjectGenerater.Instance.Fetch(_type);
            }
            else
            {
                entity = Activator.CreateInstance(_type) as Entity;
            }

            entity.IsFromPool = _isFromPool;
            entity.IsCreated = true;
            entity.IsNew = true;
            entity.InstanceID = 0;
            return entity;
        }
    }
}